<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="description" content="迎着朝阳的博客" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    MyBatis源码解读(一)配置加载 |  迎着朝阳
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="https://dxysun.com/static/yan.png" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  
<script>
var _hmt = _hmt || [];
(function() {
	var hm = document.createElement("script");
	hm.src = "https://hm.baidu.com/hm.js?aa994a8d65700b8835787dd39d079d7e";
	var s = document.getElementsByTagName("script")[0]; 
	s.parentNode.insertBefore(hm, s);
})();
</script>


</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-mybatisForSource1"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  MyBatis源码解读(一)配置加载
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2021/05/23/mybatisForSource1/" class="article-date">
  <time datetime="2021-05-23T12:21:02.000Z" itemprop="datePublished">2021-05-23</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/mybatis/">mybatis</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">4.8k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">22 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <p>MyBatis源码解读之配置加载</p>
<a id="more"></a>



<h1 id="MyBatis源码之配置加载"><a href="#MyBatis源码之配置加载" class="headerlink" title="MyBatis源码之配置加载"></a>MyBatis源码之配置加载</h1><p>从编程式的demo入手</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test3</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		<span class="comment">// 1.获取配置文件</span></span><br><span class="line">		InputStream in = Resources.getResourceAsStream(<span class="string">"mybatis-config.xml"</span>);</span><br><span class="line">		<span class="comment">// 2.加载解析配置文件并获取SqlSessionFactory对象 全局配置文件解析 映射文件解析</span></span><br><span class="line">		SqlSessionFactory factory = <span class="keyword">new</span> SqlSessionFactoryBuilder().build(in);</span><br><span class="line">		<span class="comment">// 3.根据SqlSessionFactory对象获取SqlSession对象</span></span><br><span class="line">		SqlSession sqlSession = factory.openSession();</span><br><span class="line">		<span class="comment">// 4.通过SqlSession中提供的 API方法来操作数据库</span></span><br><span class="line">		UserMapper mapper = sqlSession.getMapper(UserMapper<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">		List&lt;User&gt; list = mapper.selectUserByName(<span class="string">"zhangsan"</span>);</span><br><span class="line">		System.out.println(<span class="string">"查询结果"</span>);</span><br><span class="line">		<span class="keyword">for</span> (User user : list)</span><br><span class="line">		&#123;</span><br><span class="line">			System.out.println(user);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="comment">// 5.关闭会话</span></span><br><span class="line">		sqlSession.close();</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>思考以下几个问题</p>
<p>第一步获取配置文件，这个就不再解读了，就是读取文件流。</p>
<p>第二步，我们创建一个工厂类，配置文件的解析就是在这一步完成的，包括mybatis-config.xml和Mapper映射器文件。</p>
<p>这一步我们关心的内容：解析的时候做了什么，产生了什么对象，解析的结果存放到了哪里。</p>
<p>解析的结果决定着我们后面有什么对象可以使用，和到哪里去取。</p>
<p>第三步，通过SqlSessionFactory创建一个SqlSession。</p>
<p>问题：SqlSession上面定义各种增删改查用的。返回了什么实现类？除了SqlSession，还创建了什么对象，创建了什么环境？</p>
<p>第四步，获得一个Mapper对象。</p>
<p>问题：Mapper是一个接口，没有实现类，是不能被实例化的，那获取到的这个Mapper对象是什么对象？为什么要从SqlSession里面去获取？为什么传进去一个接口，然后还要用接口类型来接收？</p>
<p>这一步实际做的事情是执行SQL，那它又是根据什么找到XML映射器里面的SQL的？此外，我们的方法参数（对象或者Map）是怎么转换成SQL参数的？获取到的结果集是怎么转换成对象的？</p>
<p>最后一步，关闭session，这一步是必须要做的。</p>
<p>带着这几个问题，来看一下源码</p>
<h2 id="配置解析过程"><a href="#配置解析过程" class="headerlink" title="配置解析过程"></a>配置解析过程</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SqlSessionFactory factory = <span class="keyword">new</span> SqlSessionFactoryBuilder().build(in);</span><br></pre></td></tr></table></figure>

<p>首先我们new了一个Sq|SessionFactoryBuilder，这是建造者模式的运用（建造者模式用来创建复杂对象，而不需要关注内部细节，是一种封装的体现）。MyBatis中很多地方用到了建造者模式（名字以Builder结尾的类还有9个）。</p>
<p>SqlSessionFactoryBuilder中用来创建Sq|SessionFactory对象的方法是build()，build()方法有9个重载，可以用不同的方式来创建SqlSessionFactory对象。SqlSessionFactory对象默认是单例的。<br><img src="https://tu.dxysun.com/image-20210523191945988-20210523191947.png" alt="image-20210523191945988"></p>
<p>进入build()方法，最后走到这里</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> SqlSessionFactory <span class="title">build</span><span class="params">(InputStream inputStream, String environment, Properties properties)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="comment">// 用于解析 mybatis-config.xml，同时创建了 Configuration 对象 &gt;&gt;</span></span><br><span class="line">      XMLConfigBuilder parser = <span class="keyword">new</span> XMLConfigBuilder(inputStream, environment, properties);</span><br><span class="line">      <span class="comment">// 解析XML，最终返回一个 DefaultSqlSessionFactory &gt;&gt;</span></span><br><span class="line">      <span class="keyword">return</span> build(parser.parse());</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">      <span class="keyword">throw</span> ExceptionFactory.wrapException(<span class="string">"Error building SqlSession."</span>, e);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">      ErrorContext.instance().reset();</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        inputStream.close();</span><br><span class="line">      &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">        <span class="comment">// Intentionally ignore. Prefer previous error.</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>这里面创建了一个XMLConfigBuilder对象（用来存放所有配置信息的Configuration对象也是这个时候创建的）。</p>
<p>XMLConfigBuilder是抽象类BaseBuilder的一个子类，专门用来解析全局配置文件，针对不同的构建目标还有其他的一些子类（关联到源码路径），比如</p>
<p>XMLMapperBuilder：解析Mapper映射器</p>
<p>XMLStatementBuilder：解析增删改查标签</p>
<p>XMLScriptBuilder：解析动态SQL</p>
<p>最后会调用parse()方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Configuration <span class="title">parse</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (parsed) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> BuilderException(<span class="string">"Each XMLConfigBuilder can only be used once."</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  parsed = <span class="keyword">true</span>;</span><br><span class="line">  <span class="comment">// XPathParser，dom 和 SAX 都有用到 &gt;&gt;</span></span><br><span class="line">  parseConfiguration(parser.evalNode(<span class="string">"/configuration"</span>));</span><br><span class="line">  <span class="keyword">return</span> configuration;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>首先会检查全局配置文件是不是已经解析过 ，也就是说在应用的生命周期里面，config配置文件只需要解析一次，生成的Configuration对象也会存在应用的整个生命周期中。</p>
<p>接下来就是parseConfiguration方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">parseConfiguration(parser.evalNode(<span class="string">"/configuration"</span>));</span><br></pre></td></tr></table></figure>

<p>解析XML有很多方法，MyBatis对dom和SAX做了封装，方便使用。</p>
<p>这下面有十几个方法，对应着config文件里面的所有一级标签。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">parseConfiguration</span><span class="params">(XNode root)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="comment">//issue #117 read properties first</span></span><br><span class="line">      <span class="comment">// 对于全局配置文件各种标签的解析</span></span><br><span class="line">      propertiesElement(root.evalNode(<span class="string">"properties"</span>));</span><br><span class="line">      <span class="comment">// 解析 settings 标签</span></span><br><span class="line">      Properties settings = settingsAsProperties(root.evalNode(<span class="string">"settings"</span>));</span><br><span class="line">      <span class="comment">// 读取文件</span></span><br><span class="line">      loadCustomVfs(settings);</span><br><span class="line">      <span class="comment">// 日志设置</span></span><br><span class="line">      loadCustomLogImpl(settings);</span><br><span class="line">      <span class="comment">// 类型别名</span></span><br><span class="line">      typeAliasesElement(root.evalNode(<span class="string">"typeAliases"</span>));</span><br><span class="line">      <span class="comment">// 插件</span></span><br><span class="line">      pluginElement(root.evalNode(<span class="string">"plugins"</span>));</span><br><span class="line">      <span class="comment">// 用于创建对象</span></span><br><span class="line">      objectFactoryElement(root.evalNode(<span class="string">"objectFactory"</span>));</span><br><span class="line">      <span class="comment">// 用于对对象进行加工</span></span><br><span class="line">      objectWrapperFactoryElement(root.evalNode(<span class="string">"objectWrapperFactory"</span>));</span><br><span class="line">      <span class="comment">// 反射工具箱</span></span><br><span class="line">      reflectorFactoryElement(root.evalNode(<span class="string">"reflectorFactory"</span>));</span><br><span class="line">      <span class="comment">// settings 子标签赋值，默认值就是在这里提供的 &gt;&gt;</span></span><br><span class="line">      settingsElement(settings);</span><br><span class="line">      <span class="comment">// read it after objectFactory and objectWrapperFactory issue #631</span></span><br><span class="line">      <span class="comment">// 创建了数据源 &gt;&gt;</span></span><br><span class="line">      environmentsElement(root.evalNode(<span class="string">"environments"</span>));</span><br><span class="line">      databaseIdProviderElement(root.evalNode(<span class="string">"databaseIdProvider"</span>));</span><br><span class="line">      typeHandlerElement(root.evalNode(<span class="string">"typeHandlers"</span>));</span><br><span class="line">      <span class="comment">// 解析引用的Mapper映射器</span></span><br><span class="line">      mapperElement(root.evalNode(<span class="string">"mappers"</span>));</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> BuilderException(<span class="string">"Error parsing SQL Mapper Configuration. Cause: "</span> + e, e);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<h3 id="propertiesElement"><a href="#propertiesElement" class="headerlink" title="propertiesElement()"></a>propertiesElement()</h3><p>第一个是解析<code>&lt;properties&gt;</code>标签，读取我们引入的外部配置文件，例如<code>db.properties</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">propertiesElement</span><span class="params">(XNode context)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (context != <span class="keyword">null</span>) &#123;</span><br><span class="line">     <span class="comment">// 创建了一个 Properties 对象，后面可以用到</span></span><br><span class="line">     Properties defaults = context.getChildrenAsProperties();</span><br><span class="line">     String resource = context.getStringAttribute(<span class="string">"resource"</span>);</span><br><span class="line">     String url = context.getStringAttribute(<span class="string">"url"</span>);</span><br><span class="line">     <span class="keyword">if</span> (resource != <span class="keyword">null</span> &amp;&amp; url != <span class="keyword">null</span>) &#123;</span><br><span class="line">       <span class="comment">// url 和 resource 不能同时存在</span></span><br><span class="line">       <span class="keyword">throw</span> <span class="keyword">new</span> BuilderException(<span class="string">"The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other."</span>);</span><br><span class="line">     &#125;</span><br><span class="line">     <span class="comment">// 加载resource或者url属性中指定的 properties 文件</span></span><br><span class="line">     <span class="keyword">if</span> (resource != <span class="keyword">null</span>) &#123;</span><br><span class="line">       defaults.putAll(Resources.getResourceAsProperties(resource));</span><br><span class="line">     &#125; <span class="keyword">else</span> <span class="keyword">if</span> (url != <span class="keyword">null</span>) &#123;</span><br><span class="line">       defaults.putAll(Resources.getUrlAsProperties(url));</span><br><span class="line">     &#125;</span><br><span class="line">     Properties vars = configuration.getVariables();</span><br><span class="line">     <span class="keyword">if</span> (vars != <span class="keyword">null</span>) &#123;</span><br><span class="line">       <span class="comment">// 和 Configuration中的 variables 属性合并</span></span><br><span class="line">       defaults.putAll(vars);</span><br><span class="line">     &#125;</span><br><span class="line">     <span class="comment">// 更新对应的属性信息</span></span><br><span class="line">     parser.setVariables(defaults);</span><br><span class="line">     configuration.setVariables(defaults);</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>这里面又有两种类型，一种是放在resource目录下的，是相对路径，一种是写的绝对路径的（url）。</p>
<p>解析的最终结果就是我们会把所有的配置信息放到名为defaults的Properties对象里面（Hashtable对象，KV存储），最后把XPathParser和Configuration的Properties属性都设置成我们填充后的Properties对象。</p>
<h3 id="settingsAsProperties"><a href="#settingsAsProperties" class="headerlink" title="settingsAsProperties()"></a>settingsAsProperties()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> Properties <span class="title">settingsAsProperties</span><span class="params">(XNode context)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (context == <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Properties();</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 获取settings节点下的所有的子节点</span></span><br><span class="line">  Properties props = context.getChildrenAsProperties();</span><br><span class="line">  <span class="comment">// Check that all settings are known to the configuration class</span></span><br><span class="line">  MetaClass metaConfig = MetaClass.forClass(Configuration<span class="class">.<span class="keyword">class</span>, <span class="title">localReflectorFactory</span>)</span>;</span><br><span class="line">  <span class="keyword">for</span> (Object key : props.keySet()) &#123;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="keyword">if</span> (!metaConfig.hasSetter(String.valueOf(key))) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> BuilderException(<span class="string">"The setting "</span> + key + <span class="string">" is not known.  Make sure you spelled it correctly (case sensitive)."</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> props;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第二个，把<code>&lt;settings&gt;</code>标签也解析成了一个Properties对象，对于<code>&lt;settings&gt;</code>标签的子标签的处理在后面（先解析，后设置）。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Properties settings = settingsAsProperties(root.evalNode(<span class="string">"settings"</span>));</span><br></pre></td></tr></table></figure>

<h3 id="loadCustomVfs-settings"><a href="#loadCustomVfs-settings" class="headerlink" title="loadCustomVfs(settings)"></a>loadCustomVfs(settings)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">loadCustomVfs</span><span class="params">(Properties props)</span> <span class="keyword">throws</span> ClassNotFoundException </span>&#123;</span><br><span class="line">  String value = props.getProperty(<span class="string">"vfsImpl"</span>);</span><br><span class="line">  <span class="keyword">if</span> (value != <span class="keyword">null</span>) &#123;</span><br><span class="line">    String[] clazzes = value.split(<span class="string">","</span>);</span><br><span class="line">    <span class="keyword">for</span> (String clazz : clazzes) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!clazz.isEmpty()) &#123;</span><br><span class="line">        <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">        Class&lt;? extends VFS&gt; vfsImpl = (Class&lt;? extends VFS&gt;)Resources.classForName(clazz);</span><br><span class="line">        configuration.setVfsImpl(vfsImpl);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>loadCustomVfs是获取Vitual File System的自定义实现类，比如要读取本地文件，或者FTP远程文件的时候，就可以用到自定义的VFS类。<br>根据<code>&lt;settings&gt;</code>标签里面的<code>&lt;vfslmpl&gt;</code>标签，生成了一个抽象类VFS的子类，在 MyBatis中有JBoss6VFS和DefaultVFS两个实现，在io包中，最后赋值到Configuration中。</p>
<h3 id="loadCustomLogImpl"><a href="#loadCustomLogImpl" class="headerlink" title="loadCustomLogImpl"></a>loadCustomLogImpl</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">loadCustomLogImpl</span><span class="params">(Properties props)</span> </span>&#123;</span><br><span class="line">  <span class="comment">// 获取 logImpl设置的 日志 类型</span></span><br><span class="line">  Class&lt;? extends Log&gt; logImpl = resolveClass(props.getProperty(<span class="string">"logImpl"</span>));</span><br><span class="line">  <span class="comment">// 设置日志</span></span><br><span class="line">  configuration.setLogImpl(logImpl);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>loadCustomLoglmpl是根据<code>&lt;loglmpl&gt;</code>标签获取日志的实现类，我们可以用到很多的日志的方案，包括LOG4J，L0G42，SLF4J等等，在logging包中。</p>
<h3 id="typeAliasesElement"><a href="#typeAliasesElement" class="headerlink" title="typeAliasesElement()"></a>typeAliasesElement()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">typeAliasesElement</span><span class="params">(XNode parent)</span> </span>&#123;</span><br><span class="line">   <span class="comment">// 放入 TypeAliasRegistry</span></span><br><span class="line">   <span class="keyword">if</span> (parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">     <span class="keyword">for</span> (XNode child : parent.getChildren()) &#123;</span><br><span class="line">       <span class="keyword">if</span> (<span class="string">"package"</span>.equals(child.getName())) &#123;</span><br><span class="line">         String typeAliasPackage = child.getStringAttribute(<span class="string">"name"</span>);</span><br><span class="line">         configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">         String alias = child.getStringAttribute(<span class="string">"alias"</span>);</span><br><span class="line">         String type = child.getStringAttribute(<span class="string">"type"</span>);</span><br><span class="line">         <span class="keyword">try</span> &#123;</span><br><span class="line">           Class&lt;?&gt; clazz = Resources.classForName(type);</span><br><span class="line">           <span class="keyword">if</span> (alias == <span class="keyword">null</span>) &#123;</span><br><span class="line">             <span class="comment">// 扫描 @Alias 注解使用</span></span><br><span class="line">             typeAliasRegistry.registerAlias(clazz);</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">             <span class="comment">// 直接注册</span></span><br><span class="line">             typeAliasRegistry.registerAlias(alias, clazz);</span><br><span class="line">           &#125;</span><br><span class="line">         &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> BuilderException(<span class="string">"Error registering typeAlias for '"</span> + alias + <span class="string">"'. Cause: "</span> + e, e);</span><br><span class="line">         &#125;</span><br><span class="line">       &#125;</span><br><span class="line">     &#125;</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>这一步解析的是类型别名，它有两种定义方式，一种是直接定义一个类的别名（例如com.domain.Blog定义成blog），一种就是指定一个package，那么这个包下面所有的类的名字就会成为这个类全路径的别名。</p>
<p>类的别名和类的关系，放在一个TypeAliasRegistry对象里面。</p>
<h3 id="pluginElement"><a href="#pluginElement" class="headerlink" title="pluginElement()"></a>pluginElement()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">pluginElement</span><span class="params">(XNode parent)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">     <span class="keyword">for</span> (XNode child : parent.getChildren()) &#123;</span><br><span class="line">       <span class="comment">// 获取&lt;plugin&gt; 节点的 interceptor 属性的值</span></span><br><span class="line">       String interceptor = child.getStringAttribute(<span class="string">"interceptor"</span>);</span><br><span class="line">       <span class="comment">// 获取&lt;plugin&gt; 下的所有的properties子节点</span></span><br><span class="line">       Properties properties = child.getChildrenAsProperties();</span><br><span class="line">       <span class="comment">// 获取 Interceptor 对象</span></span><br><span class="line">       Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();</span><br><span class="line">       <span class="comment">// 设置 interceptor的 属性</span></span><br><span class="line">       interceptorInstance.setProperties(properties);</span><br><span class="line">       <span class="comment">// Configuration中记录 Interceptor</span></span><br><span class="line">       configuration.addInterceptor(interceptorInstance);</span><br><span class="line">     &#125;</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>接下来就是解析<code>&lt;plugins&gt;</code>标签，比如Pagehelper的翻页插件，或者我们自定义的插件。<code>&lt;plugins&gt;</code>标签里面只有<code>&lt;plugin&gt;</code>标签，<code>&lt;plugin&gt;</code>标签里面只有<code>&lt;property&gt;</code>标签。</p>
<p>因为所有的插件都要实现Interceptor接口，所以这一步的事情就是把插件解析成Interceptor类，设置属性，然后添加到Configuration的InterceptorChain属性里面，它是一个List。</p>
<h3 id="objectFactoryElement"><a href="#objectFactoryElement" class="headerlink" title="objectFactoryElement()"></a>objectFactoryElement()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">objectFactoryElement</span><span class="params">(XNode context)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (context != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="comment">// 获取&lt;objectFactory&gt; 节点的 type 属性</span></span><br><span class="line">    String type = context.getStringAttribute(<span class="string">"type"</span>);</span><br><span class="line">    <span class="comment">// 获取 &lt;objectFactory&gt; 节点下的配置信息</span></span><br><span class="line">    Properties properties = context.getChildrenAsProperties();</span><br><span class="line">    <span class="comment">// 获取ObjectFactory 对象的对象 通过反射方式</span></span><br><span class="line">    ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();</span><br><span class="line">    <span class="comment">// ObjectFactory 和 对应的属性信息关联</span></span><br><span class="line">    factory.setProperties(properties);</span><br><span class="line">    <span class="comment">// 将创建的ObjectFactory对象绑定到Configuration中</span></span><br><span class="line">    configuration.setObjectFactory(factory);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>ObjectFactory用来创建返回的对象。</p>
<h3 id="objectWrapperFactoryElement"><a href="#objectWrapperFactoryElement" class="headerlink" title="objectWrapperFactoryElement()"></a>objectWrapperFactoryElement()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">objectWrapperFactoryElement</span><span class="params">(XNode context)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (context != <span class="keyword">null</span>) &#123;</span><br><span class="line">    String type = context.getStringAttribute(<span class="string">"type"</span>);</span><br><span class="line">    ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();</span><br><span class="line">    configuration.setObjectWrapperFactory(factory);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>ObjectWrapperFactory用来对对象做特殊的处理。比如：select没有写别名，查询返回的是一个Map，可以在自定义的objectWrapperFactory中把下划线命名变成驼峰命名。</p>
<h3 id="reflectorFactoryElement"><a href="#reflectorFactoryElement" class="headerlink" title="reflectorFactoryElement()"></a>reflectorFactoryElement()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">reflectorFactoryElement</span><span class="params">(XNode context)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (context != <span class="keyword">null</span>) &#123;</span><br><span class="line">    String type = context.getStringAttribute(<span class="string">"type"</span>);</span><br><span class="line">    ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();</span><br><span class="line">    configuration.setReflectorFactory(factory);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>ReflectorFactory是反射的工具箱，对反射的操作进行了封装</p>
<p>以上几个对象都是使用resolveClass创建的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();</span><br><span class="line">(ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();</span><br><span class="line">(ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();</span><br><span class="line">(ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();</span><br></pre></td></tr></table></figure>



<h3 id="settingsElement-settings"><a href="#settingsElement-settings" class="headerlink" title="settingsElement(settings)"></a>settingsElement(settings)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">settingsElement</span><span class="params">(Properties props)</span> </span>&#123;</span><br><span class="line">   configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty(<span class="string">"autoMappingBehavior"</span>, <span class="string">"PARTIAL"</span>)));</span><br><span class="line">   configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty(<span class="string">"autoMappingUnknownColumnBehavior"</span>, <span class="string">"NONE"</span>)));</span><br><span class="line">   configuration.setCacheEnabled(booleanValueOf(props.getProperty(<span class="string">"cacheEnabled"</span>), <span class="keyword">true</span>));</span><br><span class="line">   configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty(<span class="string">"proxyFactory"</span>)));</span><br><span class="line">   configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty(<span class="string">"lazyLoadingEnabled"</span>), <span class="keyword">false</span>));</span><br><span class="line">   configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty(<span class="string">"aggressiveLazyLoading"</span>), <span class="keyword">false</span>));</span><br><span class="line">   configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty(<span class="string">"multipleResultSetsEnabled"</span>), <span class="keyword">true</span>));</span><br><span class="line">   configuration.setUseColumnLabel(booleanValueOf(props.getProperty(<span class="string">"useColumnLabel"</span>), <span class="keyword">true</span>));</span><br><span class="line">   configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty(<span class="string">"useGeneratedKeys"</span>), <span class="keyword">false</span>));</span><br><span class="line">   configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty(<span class="string">"defaultExecutorType"</span>, <span class="string">"SIMPLE"</span>)));</span><br><span class="line">   configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty(<span class="string">"defaultStatementTimeout"</span>), <span class="keyword">null</span>));</span><br><span class="line">   configuration.setDefaultFetchSize(integerValueOf(props.getProperty(<span class="string">"defaultFetchSize"</span>), <span class="keyword">null</span>));</span><br><span class="line">   configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty(<span class="string">"defaultResultSetType"</span>)));</span><br><span class="line">   configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty(<span class="string">"mapUnderscoreToCamelCase"</span>), <span class="keyword">false</span>));</span><br><span class="line">   configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty(<span class="string">"safeRowBoundsEnabled"</span>), <span class="keyword">false</span>));</span><br><span class="line">   configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty(<span class="string">"localCacheScope"</span>, <span class="string">"SESSION"</span>)));</span><br><span class="line">   configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty(<span class="string">"jdbcTypeForNull"</span>, <span class="string">"OTHER"</span>)));</span><br><span class="line">   configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty(<span class="string">"lazyLoadTriggerMethods"</span>), <span class="string">"equals,clone,hashCode,toString"</span>));</span><br><span class="line">   configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty(<span class="string">"safeResultHandlerEnabled"</span>), <span class="keyword">true</span>));</span><br><span class="line">   configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty(<span class="string">"defaultScriptingLanguage"</span>)));</span><br><span class="line">   configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty(<span class="string">"defaultEnumTypeHandler"</span>)));</span><br><span class="line">   configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty(<span class="string">"callSettersOnNulls"</span>), <span class="keyword">false</span>));</span><br><span class="line">   configuration.setUseActualParamName(booleanValueOf(props.getProperty(<span class="string">"useActualParamName"</span>), <span class="keyword">true</span>));</span><br><span class="line">   configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty(<span class="string">"returnInstanceForEmptyRow"</span>), <span class="keyword">false</span>));</span><br><span class="line">   configuration.setLogPrefix(props.getProperty(<span class="string">"logPrefix"</span>));</span><br><span class="line">   configuration.setConfigurationFactory(resolveClass(props.getProperty(<span class="string">"configurationFactory"</span>)));</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>这里就是对<code>&lt;settings&gt;</code>标签里面所有子标签的处理了，前面我们已经把子标签全部转换成了Properties对象，所以在这里处理Properties对象就可以了。settings二级标签中一共26个配置，比如二级缓存、延迟加载、默认执行器类型等等。</p>
<p>需要注意的是，我们之前提到的所有的默认值，都是在这里赋值的。如果说后面我们不知道这个属性的值是什么，也可以到这一步来确认一下。</p>
<p>所有的值，都会赋值到Configuration的属性里面去。</p>
<h3 id="environmentsElement"><a href="#environmentsElement" class="headerlink" title="environmentsElement()"></a>environmentsElement()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">environmentsElement</span><span class="params">(XNode context)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (context != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (environment == <span class="keyword">null</span>) &#123;</span><br><span class="line">      environment = context.getStringAttribute(<span class="string">"default"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (XNode child : context.getChildren()) &#123;</span><br><span class="line">      String id = child.getStringAttribute(<span class="string">"id"</span>);</span><br><span class="line">      <span class="keyword">if</span> (isSpecifiedEnvironment(id)) &#123;</span><br><span class="line">        <span class="comment">// 事务工厂</span></span><br><span class="line">        TransactionFactory txFactory = transactionManagerElement(child.evalNode(<span class="string">"transactionManager"</span>));</span><br><span class="line">        <span class="comment">// 数据源工厂（例如 DruidDataSourceFactory ）</span></span><br><span class="line">        DataSourceFactory dsFactory = dataSourceElement(child.evalNode(<span class="string">"dataSource"</span>));</span><br><span class="line">        <span class="comment">// 数据源</span></span><br><span class="line">        DataSource dataSource = dsFactory.getDataSource();</span><br><span class="line">        <span class="comment">// 包含了 事务工厂和数据源的 Environment</span></span><br><span class="line">        Environment.Builder environmentBuilder = <span class="keyword">new</span> Environment.Builder(id)</span><br><span class="line">            .transactionFactory(txFactory)</span><br><span class="line">            .dataSource(dataSource);</span><br><span class="line">        <span class="comment">// 放入 Configuration</span></span><br><span class="line">        configuration.setEnvironment(environmentBuilder.build());</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这一步是解析<code>&lt;environments&gt;</code>标签。<br>一个environment就是对应一个数据源，所以在这里我们会根据配置的<code>&lt;transactionManager&gt;</code>创建一个事务工厂，根据<code>&lt;dataSource&gt;</code>标签创建一个数据源，最后把这两个对象设置成Environment对象的属性，放到Configuration里面。</p>
<h3 id="databaseldProviderElement"><a href="#databaseldProviderElement" class="headerlink" title="databaseldProviderElement()"></a>databaseldProviderElement()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">databaseIdProviderElement</span><span class="params">(XNode context)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">  DatabaseIdProvider databaseIdProvider = <span class="keyword">null</span>;</span><br><span class="line">  <span class="keyword">if</span> (context != <span class="keyword">null</span>) &#123;</span><br><span class="line">    String type = context.getStringAttribute(<span class="string">"type"</span>);</span><br><span class="line">    <span class="comment">// awful patch to keep backward compatibility</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="string">"VENDOR"</span>.equals(type)) &#123;</span><br><span class="line">      type = <span class="string">"DB_VENDOR"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    Properties properties = context.getChildrenAsProperties();</span><br><span class="line">    databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();</span><br><span class="line">    databaseIdProvider.setProperties(properties);</span><br><span class="line">  &#125;</span><br><span class="line">  Environment environment = configuration.getEnvironment();</span><br><span class="line">  <span class="keyword">if</span> (environment != <span class="keyword">null</span> &amp;&amp; databaseIdProvider != <span class="keyword">null</span>) &#123;</span><br><span class="line">    String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());</span><br><span class="line">    configuration.setDatabaseId(databaseId);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>解析databaseldProvider标签，生成DatabaseldProvider对象（用来支持不同厂商的数据库）。</p>
<h3 id="typeHandlerElement"><a href="#typeHandlerElement" class="headerlink" title="typeHandlerElement()"></a>typeHandlerElement()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">typeHandlerElement</span><span class="params">(XNode parent)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="keyword">for</span> (XNode child : parent.getChildren()) &#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="string">"package"</span>.equals(child.getName())) &#123;</span><br><span class="line">        String typeHandlerPackage = child.getStringAttribute(<span class="string">"name"</span>);</span><br><span class="line">        typeHandlerRegistry.register(typeHandlerPackage);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        String javaTypeName = child.getStringAttribute(<span class="string">"javaType"</span>);</span><br><span class="line">        String jdbcTypeName = child.getStringAttribute(<span class="string">"jdbcType"</span>);</span><br><span class="line">        String handlerTypeName = child.getStringAttribute(<span class="string">"handler"</span>);</span><br><span class="line">        Class&lt;?&gt; javaTypeClass = resolveClass(javaTypeName);</span><br><span class="line">        JdbcType jdbcType = resolveJdbcType(jdbcTypeName);</span><br><span class="line">        Class&lt;?&gt; typeHandlerClass = resolveClass(handlerTypeName);</span><br><span class="line">        <span class="keyword">if</span> (javaTypeClass != <span class="keyword">null</span>) &#123;</span><br><span class="line">          <span class="keyword">if</span> (jdbcType == <span class="keyword">null</span>) &#123;</span><br><span class="line">            typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);</span><br><span class="line">          &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);</span><br><span class="line">          &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          typeHandlerRegistry.register(typeHandlerClass);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>跟TypeAlias一样，TypeHandler有两种配置方式，一种是单独配置一个类，一种是指定一个package。最后得到的是JavaType和JdbcType，以及用来做相互映射的TypeHandler之间的映射关系，存放在TypeHandlerRegistry对象里面。</p>
<p>这三个对象（Java类型，JDBC类型，Handler）的关系怎么映射？（Map里面再放一个Map）</p>
<h3 id="mapperElement"><a href="#mapperElement" class="headerlink" title="mapperElement()"></a>mapperElement()</h3><p><a href="http://www.mybatis.org/mybatis-3/zh/configuration.html#mappers" target="_blank" rel="noopener">http://www.mybatis.org/mybatis-3/zh/configuration.html#mappers</a><br>最后就是<code>&lt;mappers&gt;</code>标签的解析。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">mapperElement</span><span class="params">(XNode parent)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">     <span class="keyword">for</span> (XNode child : parent.getChildren()) &#123;</span><br><span class="line">       <span class="comment">// 不同的定义方式的扫描，最终都是调用 addMapper()方法（添加到 MapperRegistry）。这个方法和 getMapper() 对应</span></span><br><span class="line">       <span class="comment">// package	包</span></span><br><span class="line">       <span class="keyword">if</span> (<span class="string">"package"</span>.equals(child.getName())) &#123;</span><br><span class="line">         String mapperPackage = child.getStringAttribute(<span class="string">"name"</span>);</span><br><span class="line">         configuration.addMappers(mapperPackage);</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">         String resource = child.getStringAttribute(<span class="string">"resource"</span>);</span><br><span class="line">         String url = child.getStringAttribute(<span class="string">"url"</span>);</span><br><span class="line">         String mapperClass = child.getStringAttribute(<span class="string">"class"</span>);</span><br><span class="line">         <span class="keyword">if</span> (resource != <span class="keyword">null</span> &amp;&amp; url == <span class="keyword">null</span> &amp;&amp; mapperClass == <span class="keyword">null</span>) &#123;</span><br><span class="line">           <span class="comment">// resource	相对路径</span></span><br><span class="line">           ErrorContext.instance().resource(resource);</span><br><span class="line">           InputStream inputStream = Resources.getResourceAsStream(resource);</span><br><span class="line">           XMLMapperBuilder mapperParser = <span class="keyword">new</span> XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());</span><br><span class="line">           <span class="comment">// 解析 Mapper.xml，总体上做了两件事情 &gt;&gt;</span></span><br><span class="line">           mapperParser.parse();</span><br><span class="line">         &#125; <span class="keyword">else</span> <span class="keyword">if</span> (resource == <span class="keyword">null</span> &amp;&amp; url != <span class="keyword">null</span> &amp;&amp; mapperClass == <span class="keyword">null</span>) &#123;</span><br><span class="line">           <span class="comment">// url	绝对路径</span></span><br><span class="line">           ErrorContext.instance().resource(url);</span><br><span class="line">           InputStream inputStream = Resources.getUrlAsStream(url);</span><br><span class="line">           XMLMapperBuilder mapperParser = <span class="keyword">new</span> XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());</span><br><span class="line">           mapperParser.parse();</span><br><span class="line">         &#125; <span class="keyword">else</span> <span class="keyword">if</span> (resource == <span class="keyword">null</span> &amp;&amp; url == <span class="keyword">null</span> &amp;&amp; mapperClass != <span class="keyword">null</span>) &#123;</span><br><span class="line">           <span class="comment">// class 	单个接口</span></span><br><span class="line">           Class&lt;?&gt; mapperInterface = Resources.classForName(mapperClass);</span><br><span class="line">           configuration.addMapper(mapperInterface);</span><br><span class="line">         &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> BuilderException(<span class="string">"A mapper element may only specify a url, resource or class, but not more than one."</span>);</span><br><span class="line">         &#125;</span><br><span class="line">       &#125;</span><br><span class="line">     &#125;</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>根据全局配置文件中不同的注册方式，用不同的方式扫描，但最终都是做了两件事情，对于语句的注册和接口的注册。</p>
<table>
<thead>
<tr>
<th>扫描类型</th>
<th>含义</th>
</tr>
</thead>
<tbody><tr>
<td>resource</td>
<td>相对路径</td>
</tr>
<tr>
<td>url</td>
<td>绝对路径</td>
</tr>
<tr>
<td>package</td>
<td>包</td>
</tr>
<tr>
<td>class</td>
<td>单个接口</td>
</tr>
</tbody></table>
<h3 id="mapperParser-parse"><a href="#mapperParser-parse" class="headerlink" title="mapperParser.parse()"></a>mapperParser.parse()</h3><p>先从解析Mapper.xml的mapperParser.parse()方法入手。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">parse</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="comment">// 总体上做了两件事情，对于语句的注册和接口的注册</span></span><br><span class="line">  <span class="keyword">if</span> (!configuration.isResourceLoaded(resource)) &#123;</span><br><span class="line">    <span class="comment">// 1、具体增删改查标签的解析。</span></span><br><span class="line">    <span class="comment">// 一个标签一个MappedStatement。 &gt;&gt;</span></span><br><span class="line">    configurationElement(parser.evalNode(<span class="string">"/mapper"</span>));</span><br><span class="line">    configuration.addLoadedResource(resource);</span><br><span class="line">    <span class="comment">// 2、把namespace（接口类型）和工厂类绑定起来，放到一个map。</span></span><br><span class="line">    <span class="comment">// 一个namespace 一个 MapperProxyFactory &gt;&gt;</span></span><br><span class="line">    bindMapperForNamespace();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  parsePendingResultMaps();</span><br><span class="line">  parsePendingCacheRefs();</span><br><span class="line">  parsePendingStatements();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>configurationElement() 解析所有的子标签，最终获得MappedStatement对象。</p>
<p>bindMapperForNamespace() 把namespace（接口类型）和工厂类MapperProxyFactory绑定起来</p>
<h4 id="1-configurationElement"><a href="#1-configurationElement" class="headerlink" title="(1) configurationElement()"></a>(1) configurationElement()</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">configurationElement</span><span class="params">(XNode context)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">     String namespace = context.getStringAttribute(<span class="string">"namespace"</span>);</span><br><span class="line">     <span class="keyword">if</span> (namespace == <span class="keyword">null</span> || namespace.equals(<span class="string">""</span>)) &#123;</span><br><span class="line">       <span class="keyword">throw</span> <span class="keyword">new</span> BuilderException(<span class="string">"Mapper's namespace cannot be empty"</span>);</span><br><span class="line">     &#125;</span><br><span class="line">     builderAssistant.setCurrentNamespace(namespace);</span><br><span class="line">     <span class="comment">// 添加缓存对象</span></span><br><span class="line">     cacheRefElement(context.evalNode(<span class="string">"cache-ref"</span>));</span><br><span class="line">     <span class="comment">// 解析 cache 属性，添加缓存对象</span></span><br><span class="line">     cacheElement(context.evalNode(<span class="string">"cache"</span>));</span><br><span class="line">     <span class="comment">// 创建 ParameterMapping 对象</span></span><br><span class="line">     parameterMapElement(context.evalNodes(<span class="string">"/mapper/parameterMap"</span>));</span><br><span class="line">     <span class="comment">// 创建 List&lt;ResultMapping&gt;</span></span><br><span class="line">     resultMapElements(context.evalNodes(<span class="string">"/mapper/resultMap"</span>));</span><br><span class="line">     <span class="comment">// 解析可以复用的SQL</span></span><br><span class="line">     sqlElement(context.evalNodes(<span class="string">"/mapper/sql"</span>));</span><br><span class="line">     <span class="comment">// 解析增删改查标签，得到 MappedStatement &gt;&gt;</span></span><br><span class="line">     buildStatementFromContext(context.evalNodes(<span class="string">"select|insert|update|delete"</span>));</span><br><span class="line">   &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">     <span class="keyword">throw</span> <span class="keyword">new</span> BuilderException(<span class="string">"Error parsing Mapper XML. The XML location is '"</span> + resource + <span class="string">"'. Cause: "</span> + e, e);</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>configurationElement是对Mapper.xml中所有具体标签的解析，包括namespace、 cache、parameterMap、 resultMap、sql和select|insert|update|delete。</p>
<p>在buildStatementFromContext() 方法中，创建了用来解析增删改查标签的XMLStatementBuilder，并且把创建的MappedStatement添加到 mappedStatements中。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">buildStatementFromContext</span><span class="params">(List&lt;XNode&gt; list)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (configuration.getDatabaseId() != <span class="keyword">null</span>) &#123;</span><br><span class="line">    buildStatementFromContext(list, configuration.getDatabaseId());</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 解析 Statement &gt;&gt;</span></span><br><span class="line">  buildStatementFromContext(list, <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">buildStatementFromContext</span><span class="params">(List&lt;XNode&gt; list, String requiredDatabaseId)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">for</span> (XNode context : list) &#123;</span><br><span class="line">    <span class="comment">// 用来解析增删改查标签的 XMLStatementBuilder</span></span><br><span class="line">    <span class="keyword">final</span> XMLStatementBuilder statementParser = <span class="keyword">new</span> XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="comment">// 解析 Statement，添加 MappedStatement 对象 &gt;&gt;</span></span><br><span class="line">      statementParser.parseStatementNode();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IncompleteElementException e) &#123;</span><br><span class="line">      configuration.addIncompleteStatement(statementParser);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="2-bindMapperForNamespace"><a href="#2-bindMapperForNamespace" class="headerlink" title="(2) bindMapperForNamespace()"></a>(2) bindMapperForNamespace()</h4><p>主要是是调用了addMapper()。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">bindMapperForNamespace</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   String namespace = builderAssistant.getCurrentNamespace();</span><br><span class="line">   <span class="keyword">if</span> (namespace != <span class="keyword">null</span>) &#123;</span><br><span class="line">     Class&lt;?&gt; boundType = <span class="keyword">null</span>;</span><br><span class="line">     <span class="keyword">try</span> &#123;</span><br><span class="line">       <span class="comment">// 根据名称空间加载对应的接口类型</span></span><br><span class="line">       boundType = Resources.classForName(namespace);</span><br><span class="line">     &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">       <span class="comment">//ignore, bound type is not required</span></span><br><span class="line">     &#125;</span><br><span class="line">     <span class="keyword">if</span> (boundType != <span class="keyword">null</span>) &#123;</span><br><span class="line">       <span class="comment">// 判断 在MapperRegistry中是否注册的有当前类型的 MapperProxyFactory对象</span></span><br><span class="line">       <span class="keyword">if</span> (!configuration.hasMapper(boundType)) &#123;</span><br><span class="line">         <span class="comment">// Spring may not know the real resource name so we set a flag</span></span><br><span class="line">         <span class="comment">// to prevent loading again this resource from the mapper interface</span></span><br><span class="line">         <span class="comment">// look at MapperAnnotationBuilder#loadXmlResource</span></span><br><span class="line">         configuration.addLoadedResource(<span class="string">"namespace:"</span> + namespace);</span><br><span class="line">         <span class="comment">// 添加到 MapperRegistry，本质是一个 map，里面也有 Configuration &gt;&gt;</span></span><br><span class="line">         configuration.addMapper(boundType);</span><br><span class="line">       &#125;</span><br><span class="line">     &#125;</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>addMapper() 方法中，把接口类型注册到MapperRegistry中，实际上是为接口创建一个对应的MapperProxyFactory（用于为这个type提供工厂类，创建MapperProxy)。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;T&gt; <span class="function"><span class="keyword">void</span> <span class="title">addMapper</span><span class="params">(Class&lt;T&gt; type)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (type.isInterface()) &#123; <span class="comment">// 检测 type 是否为接口</span></span><br><span class="line">    <span class="keyword">if</span> (hasMapper(type)) &#123; <span class="comment">// 检测是否已经加装过该接口</span></span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> BindingException(<span class="string">"Type "</span> + type + <span class="string">" is already known to the MapperRegistry."</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">boolean</span> loadCompleted = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="comment">// ！Map&lt;Class&lt;?&gt;, MapperProxyFactory&lt;?&gt;&gt; 存放的是接口类型，和对应的工厂类的关系</span></span><br><span class="line">      knownMappers.put(type, <span class="keyword">new</span> MapperProxyFactory&lt;&gt;(type));</span><br><span class="line">      <span class="comment">// It's important that the type is added before the parser is run</span></span><br><span class="line">      <span class="comment">// otherwise the binding may automatically be attempted by the</span></span><br><span class="line">      <span class="comment">// mapper parser. If the type is already known, it won't try.</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 注册了接口之后，根据接口，开始解析所有方法上的注解，例如 @Select &gt;&gt;</span></span><br><span class="line">      MapperAnnotationBuilder parser = <span class="keyword">new</span> MapperAnnotationBuilder(config, type);</span><br><span class="line">      parser.parse();</span><br><span class="line">      loadCompleted = <span class="keyword">true</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (!loadCompleted) &#123;</span><br><span class="line">        knownMappers.remove(type);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注册了接口之后，开始解析接口类和所有方法上的注解，例如@CacheNamespace、@Select。</p>
<p>此处创建了一个MapperAnnotationBuilder专门用来解析注解。</p>
<h4 id="3-parse"><a href="#3-parse" class="headerlink" title="(3)parse"></a>(3)parse</h4><p>parse() 方法中的parseCache()和parseCacheRef()方法其实是对@CacheNamespace和@CacheNamespaceRef这两个注解的处理。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">parse</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   String resource = type.toString();</span><br><span class="line">   <span class="keyword">if</span> (!configuration.isResourceLoaded(resource)) &#123;</span><br><span class="line">     <span class="comment">// 先判断 Mapper.xml 有没有解析，没有的话先解析 Mapper.xml（例如定义 package 方式）</span></span><br><span class="line">     loadXmlResource();</span><br><span class="line">     configuration.addLoadedResource(resource);</span><br><span class="line">     assistant.setCurrentNamespace(type.getName());</span><br><span class="line">     <span class="comment">// 处理 @CacheNamespace</span></span><br><span class="line">     parseCache();</span><br><span class="line">     <span class="comment">// 处理 @CacheNamespaceRef</span></span><br><span class="line">     parseCacheRef();</span><br><span class="line">     <span class="comment">// 获取所有方法</span></span><br><span class="line">     Method[] methods = type.getMethods();</span><br><span class="line">     <span class="keyword">for</span> (Method method : methods) &#123;</span><br><span class="line">       <span class="keyword">try</span> &#123;</span><br><span class="line">         <span class="comment">// issue #237</span></span><br><span class="line">         <span class="keyword">if</span> (!method.isBridge()) &#123;</span><br><span class="line">           <span class="comment">// 解析方法上的注解，添加到 MappedStatement 集合中 &gt;&gt;</span></span><br><span class="line">           parseStatement(method);</span><br><span class="line">         &#125;</span><br><span class="line">       &#125; <span class="keyword">catch</span> (IncompleteElementException e) &#123;</span><br><span class="line">         configuration.addIncompleteMethod(<span class="keyword">new</span> MethodResolver(<span class="keyword">this</span>, method));</span><br><span class="line">       &#125;</span><br><span class="line">     &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   parsePendingMethods();</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>parseStatement() 方法里面的各种getAnnotation()，都是对相应的注解的解析，比如@Options，@SelectKey，@ResultMap等等。</p>
<p>最后同样会创建MappedStatement对象，添加到MapperRegistry中。也就是说在XML中配置，和使用注解配置，最后起到一样的效果。</p>
<h4 id="4-build"><a href="#4-build" class="headerlink" title="(4) build"></a>(4) build</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> SqlSessionFactory <span class="title">build</span><span class="params">(Configuration config)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> DefaultSqlSessionFactory(config);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Mapper.xml解析完之后，调用另一个build() 方法，返回SqlSessionFactory的默认实现类DefaultSqISessionFactory。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在这一步，mybatis主要完成了config配置文件、Mapper文件、Mapper接口中注解的解析。</p>
<p>得到了一个最重要的对象Configuration，这里面存放了全部的配置信息，它在属性里面还有各种各样的容器。</p>
<p>最后，返回了一个DefaultSqlSessionFactory，里面持有了Configuration的实例。</p>
<p><img src="https://tu.dxysun.com/lALPGpNybN3o6U3NA7nNBIs_1163_953-20210523203107.png" alt=""></p>
 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          打赏
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://dxysun.com/2021/05/23/mybatisForSource1/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/mybatis/" rel="tag">mybatis</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2021/05/23/mybatisForSource2/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            MyBatis源码解读(二)会话创建
          
        </div>
      </a>
    
    
      <a href="/2021/05/23/mybatisForSource/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">MyBatis体系结构</div>
      </a>
    
  </nav>

  
   
  
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2024
        <i class="ri-heart-fill heart_icon"></i> dxysun
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
        <li>
          <a href="https://beian.miit.gov.cn" target="_black" rel="nofollow">豫ICP备17012675号-1</a>
        </li>
        
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="https://dxysun.com/static/logo.png" alt="迎着朝阳"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/photos">相册</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/alipay-20201219151322.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/weixin-20201219151346.png">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
      tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
      }
  });

  MathJax.Hub.Queue(function() {
      var all = MathJax.Hub.getAllJax(), i;
      for(i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
      }
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.6/unpacked/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
  var ayerConfig = {
    mathjax: true
  }
</script>

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


<script src="/js/dz.js"></script>



    
  </div>
</body>

</html>